#include "dameon.h"
#include "snetwork/NluHttpResponse.h"
#include "snetwork/NluEvent.h"
#include "snetwork/NluList.h"


namespace Sock
{   
    CNluHttpResponse *CNluHttpResponse::m_instance = NULL;

    CNluHttpResponse *CNluHttpResponse::instance()
    {
        if (m_instance==NULL) {
            m_instance = new CNluHttpResponse();
        }
        return m_instance;
    }
    
    CNluHttpResponse::CNluHttpResponse()
    {
        
    }    
    
    /**
     *  http_chain_t *p     save current element
     *  http_chain_t *q     save next element
     */
    int CNluHttpResponse::execute(nl_http_request_t *r)    
    {
        try {
            CNluConnection conn;
            http_chain_t *p, *q;
            nl_event_t      *wev;
            wev = (nl_event_t *) r->connection->write;
            llog("r->out->buf->file->fd :%d", r->out->buf->file->fd );			    
            p = r->out_head->next;            
            while ( p ) {//&& p->buf->start
            
                // for test
                //llog("p->buf->start:\n%s", p->buf->start); 
                
                //llog("wev->ready: %d", wev->ready);                   
                if (p->buf->in_file) {
                    llog("p->buf->file->fd :%d", p->buf->file->fd );
                    llog("in_file true");
                }
                else {
                    llog("in_file false");
                }
               
                // need to response element
                r->header_out = p->buf;
                this->HttpWriteResponseHeader(r);
                
                // save next element
                q = p->next;
                
                CNluList::instance()->CListDelete_L(r->out_head, p);
                                
                // delete element from the list           
                if (r->out_head->next == NULL) {
                    // the struct become empty
                    r->out = r->out_head;
                }
                               
                // loot to next element
                p = q;
                
                //conn.freeConnection(nlu_cycle_t *cycle, nlu_connection_t *c)
            }
		}
		catch (CThrowException e) {
			llog("Error: %s", e.getMessage().c_str());
		}
		
        return 0;
    }
    
    ssize_t CNluHttpResponse::HttpWriteResponseHeader(nl_http_request_t *r)
    {
        ssize_t n;
        nl_connection_t *c;
        nl_event_t *wev;

        c = r->connection;
        wev = (nl_event_t *) c->write;

        //llog("wev->ready: %d", wev->ready);
        if (r->header_out->end - r->header_out->last <= 0) {
            return 0;
        }                        
                
        if (wev->ready) {
            if (r->header_out->in_file) {
                n = fileSend(c, r->header_out, r->header_out->end - r->header_out->last);
            }
            else {
                n = unixSend(c, r->header_out->last,
                    r->header_out->end - r->header_out->last);
            }
        }
        else {
            n = HTTP_AGAIN;
        }        

        if (n == HTTP_AGAIN) {
            /*
            if (ngx_handle_write_event(rev, 0) != HTTP_OK) {
                ngx_http_close_request(r, HTTP_INTERNAL_SERVER_ERROR);
                return HTTP_ERROR;
            }
            */
            return HTTP_AGAIN;
        }

        if (n == 0) {
            llog("client closed prematurely connection");
        }

        if (n == 0 || n == HTTP_ERROR) {
            return HTTP_ERROR;
        }
        
        //llog("r->header_out->end - r->header_out->last:%d", r->header_out->end - r->header_out->last);
        //llog("n:%d", n);
        
        r->header_out->last += n;
        llog("r->header_out->end - r->header_out->last:%d", r->header_out->end - r->header_out->last);
        return n;
    }
        
    ssize_t CNluHttpResponse::unixSend(nl_connection_t *c, char *buf, size_t size)
    {
        ssize_t         n;
        nl_event_t      *wev;

        wev = (nl_event_t *) c->write;

        for ( ;; ) {
            n = send(c->fd, buf, size, 0);

            llog("send: fd:%d %d of %d", c->fd, n, size);
            if (n > 0) {
                if (n < (ssize_t) size) {
                    wev->ready = 0;
                }
                c->sent += n;
                return n;
            }

            if (n == 0) {
                llog("send() returned zero");
                wev->ready = 0;
                return n;
            }

            if (errno == EAGAIN || errno == EINTR) {
                wev->ready = 0;

                llog("send() not ready");
                if (errno == EAGAIN) {
                    return HTTP_AGAIN;
                }
            }
            else {
                wev->error = 1;
                llog("send() failed");
                return HTTP_ERROR;
            }
        }
    }
    
    ssize_t CNluHttpResponse::fileSend(nl_connection_t *c, http_buf_t *b, size_t size)
    {
        ssize_t         n;
        nl_event_t      *wev;
        
        wev = (nl_event_t *) c->write;
        for ( ;; ) {
            llog("c->fd:%d, b->file->fd:%d", c->fd, b->file->fd);
            n = sendfile(c->fd, b->file->fd , 0, size);
            llog("send: fd:%d %d of %d", c->fd, n, size);
            if (n > 0) {
                if (n < (ssize_t) size) {
                    wev->ready = 0;
                }
                
                c->sent += n;
                return n;
            }

            if (n == 0) {
                llog("send() returned zero");
                wev->ready = 0;
                return n;
            }

            if (errno == EAGAIN || errno == EINTR) {
                wev->ready = 0;

                llog("send() not ready");
                if (errno == EAGAIN) {
                    return HTTP_AGAIN;
                }
            }
            else {
                wev->error = 1;
                llog("send() failed");
                return HTTP_ERROR;
            }
        }
    }        

}
